/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#include "gamelib.h"


/*
 ==================
 ByteBuffer::Init
 ==================
*/
ByteBuffer::ByteBuffer(byte *data, int size, bool allowOverflow){
	Init(data,size,allowOverflow);
}

void ByteBuffer::Init(byte *data, int size, bool allowOverflow){
	this->data = data;
	this->size = size;
	this->allowOverflow = allowOverflow;
	this->overflowed = false;

	this->writeCount = 0;
	this->writeBytes = 0;
	this->writeBit = 0;

	this->readCount = 0;
	this->readBytes = 0;
	this->readBit = 0;
}
/*
 ==============================================================================

 WRITING FUNCTIONS

 ==============================================================================
*/


/*
 ==================
 ByteBuffer::ClearWriteState
 ==================
*/
void ByteBuffer::ClearWriteState (){

	this->overflowed = false;

	this->writeCount = 0;
	this->writeBytes = 0;
	this->writeBit = 0;
}

/*
 ==================
 ByteBuffer::SetWriteState
 ==================
*/
void ByteBuffer::SetWriteState (int bits){

	if (bits > (this->size << 3))
		throw Exception("ByteBuffer::SetWriteState: bad bits (%i)", bits);

	this->overflowed = false;

	this->writeCount = bits;
	this->writeBytes = (bits + 7) >> 3;
	this->writeBit = bits & 7;
}

/*
 ==================
 ByteBuffer::WriteBits
 ==================
*/
void ByteBuffer::WriteBits (int value, int bits){

	int		write, count;

	if (bits < 1 || bits > 32)
		throw Exception("ByteBuffer::WriteBits: bad bits (%i)", bits);

	// Check for overflow
	if (this->overflowed)
		return;			// Already overflowed

	if (this->writeCount + bits > (this->size << 3)){
		if (!this->allowOverflow)
			throw Exception("ByteBuffer::WriteBits: overflow without allowOverflow set");

		this->overflowed = true;

		return;
	}

	this->writeCount += bits;

	// Write the bits
	while (bits){
		if (this->writeBit == 0){
			if (bits >= 8){
				write = value & 255;

				this->data[this->writeBytes++] = write;

				value >>= 8;
				bits -= 8;

				continue;
			}

			this->data[this->writeBytes++] = 0;
		}

		count = 8 - this->writeBit;
		if (count > bits)
			count = bits;

		write = value & (BIT(count) - 1);

		this->data[this->writeBytes - 1] |= write << this->writeBit;
		this->writeBit = (this->writeBit + count) & 7;

		value >>= count;
		bits -= count;
	}
}

/*
 ==================
 ByteBuffer::WriteBool
 ==================
*/
void ByteBuffer::WriteBool (bool value){

	ByteBuffer::WriteBits((value != false), 1);
}

/*
 ==================
 ByteBuffer::WriteChar
 ==================
*/
void ByteBuffer::WriteChar (int value){

	ByteBuffer::WriteBits(value, 8);
}

/*
 ==================
 ByteBuffer::WriteByte
 ==================
*/
void ByteBuffer::WriteByte (int value){

	ByteBuffer::WriteBits(value, 8);
}

/*
 ==================
 ByteBuffer::WriteShort
 ==================
*/
void ByteBuffer::WriteShort (int value){

	ByteBuffer::WriteBits(value, 16);
}

/*
 ==================
 ByteBuffer::WriteLong
 ==================
*/
void ByteBuffer::WriteLong (int value){

	ByteBuffer::WriteBits(value, 32);
}

/*
 ==================
 ByteBuffer::WriteFloat
 ==================
*/
void ByteBuffer::WriteFloat (float value){

	ByteBuffer::WriteBits(*(int *)&value, 32);
}

/*
 ==================
 ByteBuffer::WriteAngle8
 ==================
*/
void ByteBuffer::WriteAngle8 (float angle){

	ByteBuffer::WriteBits(ANGLE2BYTE(angle), 8);
}

/*
 ==================
 ByteBuffer::WriteAngle16
 ==================
*/
void ByteBuffer::WriteAngle16 (float angle){

	ByteBuffer::WriteBits(ANGLE2SHORT(angle), 16);
}

/*
 ==================
 ByteBuffer::WriteDir
 ==================
*/
void ByteBuffer::WriteDir (const odVec3 &dir){

	byte	bytes[2];

	M_NormalToBytes(dir, bytes);

	ByteBuffer::WriteBits(bytes[0], 8);
	ByteBuffer::WriteBits(bytes[1], 8);
}

/*
 ==================
 ByteBuffer::WriteString
 ==================
*/
void ByteBuffer::WriteString (const char *string){

	while (*string){
		ByteBuffer::WriteBits(*string, 8);

		string++;
	}

	ByteBuffer::WriteBits(0, 8);
}

/*
 ==================
 ByteBuffer::WriteData
 ==================
*/
void ByteBuffer::WriteData (const void *data, int size){

	int		i = 0;

	while (size >= 4){
		ByteBuffer::WriteBits(((const uint *)data)[i++], 32);

		size -= 4;
	}

	i <<= 2;

	while (size){
		ByteBuffer::WriteBits(((const byte *)data)[i++], 8);

		size--;
	}
}

/*
 ==================
 ByteBuffer::WriteDeltaChar
 ==================
*/
void ByteBuffer::WriteDeltaChar (int oldValue, int newValue){

	if (oldValue == newValue){
		ByteBuffer::WriteBits(0, 1);
		return;
	}

	ByteBuffer::WriteBits(1, 1);
	ByteBuffer::WriteBits(newValue, 8);
}

/*
 ==================
 ByteBuffer::WriteDeltaByte
 ==================
*/
void ByteBuffer::WriteDeltaByte (int oldValue, int newValue){

	if (oldValue == newValue){
		ByteBuffer::WriteBits(0, 1);
		return;
	}

	ByteBuffer::WriteBits(1, 1);
	ByteBuffer::WriteBits(newValue, 8);
}

/*
 ==================
 ByteBuffer::WriteDeltaShort
 ==================
*/
void ByteBuffer::WriteDeltaShort (int oldValue, int newValue){

	if (oldValue == newValue){
		ByteBuffer::WriteBits(0, 1);
		return;
	}

	ByteBuffer::WriteBits(1, 1);
	ByteBuffer::WriteBits(newValue, 16);
}

/*
 ==================
 ByteBuffer::WriteDeltaLong
 ==================
*/
void ByteBuffer::WriteDeltaLong (int oldValue, int newValue){

	if (oldValue == newValue){
		ByteBuffer::WriteBits(0, 1);
		return;
	}

	ByteBuffer::WriteBits(1, 1);
	ByteBuffer::WriteBits(newValue, 32);
}

/*
 ==================
 ByteBuffer::WriteDeltaFloat
 ==================
*/
void ByteBuffer::WriteDeltaFloat (float oldValue, float newValue){

	if (oldValue == newValue){
		ByteBuffer::WriteBits(0, 1);
		return;
	}

	ByteBuffer::WriteBits(1, 1);
	ByteBuffer::WriteBits(*(int *)&newValue, 32);
}

/*
 ==================
 ByteBuffer::WriteDeltaAngle8
 ==================
*/
void ByteBuffer::WriteDeltaAngle8 (float oldAngle, float newAngle){

	if (oldAngle == newAngle){
		ByteBuffer::WriteBits(0, 1);
		return;
	}

	ByteBuffer::WriteBits(1, 1);
	ByteBuffer::WriteBits(ANGLE2BYTE(newAngle), 8);
}

/*
 ==================
 ByteBuffer::WriteDeltaAngle16
 ==================
*/
void ByteBuffer::WriteDeltaAngle16 (float oldAngle, float newAngle){

	if (oldAngle == newAngle){
		ByteBuffer::WriteBits(0, 1);
		return;
	}

	ByteBuffer::WriteBits(1, 1);
	ByteBuffer::WriteBits(ANGLE2SHORT(newAngle), 16);
}

/*
 ==================
 ByteBuffer::WriteDeltaDir
 ==================
*/
void ByteBuffer::WriteDeltaDir (const odVec3 &oldDir, const odVec3 &newDir){

	byte	bytes[2];

	if (oldDir == newDir){
		ByteBuffer::WriteBits(0, 1);
		return;
	}

	M_NormalToBytes(newDir, bytes);

	ByteBuffer::WriteBits(1, 1);
	ByteBuffer::WriteBits(bytes[0], 8);
	ByteBuffer::WriteBits(bytes[1], 8);
}


/*
 ==============================================================================

 READING FUNCTIONS

 ==============================================================================
*/


/*
 ==================
 ByteBuffer::ClearReadState
 ==================
*/
void ByteBuffer::ClearReadState (){

	this->readCount = 0;
	this->readBytes = 0;
	this->readBit = 0;
}

/*
 ==================
 ByteBuffer::SetReadState
 ==================
*/
void ByteBuffer::SetReadState (int bits){

	if (bits > this->writeCount)
		throw Exception("ByteBuffer::SetReadState: bad bits (%i)", bits);

	this->readCount = bits;
	this->readBytes = (bits + 7) >> 3;
	this->readBit = bits & 7;
}

/*
 ==================
 ByteBuffer::ReadBits
 ==================
*/
int ByteBuffer::ReadBits (int bits){

	int		read, count;
	int		shift = 0;
	int		value = 0;

	if (bits < 1 || bits > 32)
		throw Exception("ByteBuffer::ReadBits: bad bits (%i)", bits);

	// Check for overflow
	if (this->readCount > this->writeCount)
		return -1;		// Already overflowed

	if (this->readCount + bits > this->writeCount){
		// Overflowed
		this->readCount = this->readCount + bits;
		this->readBytes = (this->readCount + 7) >> 3;
		this->readBit = this->readCount & 7;

		return -1;
	}

	this->readCount += bits;

	// Read the bits
	while (bits){
		if (this->readBit == 0){
			if (bits >= 8){
				read = this->data[this->readBytes++];

				value |= read << shift;

				shift += 8;
				bits -= 8;

				continue;
			}

			this->readBytes++;
		}

		count = 8 - this->readBit;
		if (count > bits)
			count = bits;

		read = (this->data[this->readBytes - 1] >> this->readBit) & (BIT(count) - 1);
		this->readBit = (this->readBit + count) & 7;

		value |= read << shift;

		shift += count;
		bits -= count;
	}

	return value;
}

/*
 ==================
 ByteBuffer::ReadBool
 ==================
*/
bool ByteBuffer::ReadBool (){

	return (bool)ByteBuffer::ReadBits(1);
}

/*
 ==================
 ByteBuffer::ReadChar
 ==================
*/
int ByteBuffer::ReadChar (){

	return (char)ByteBuffer::ReadBits(8);
}

/*
 ==================
 ByteBuffer::ReadByte
 ==================
*/
int ByteBuffer::ReadByte (){

	return (byte)ByteBuffer::ReadBits(8);
}

/*
 ==================
 ByteBuffer::ReadShort
 ==================
*/
int ByteBuffer::ReadShort (){

	return (short)ByteBuffer::ReadBits(16);
}

/*
 ==================
 ByteBuffer::ReadLong
 ==================
*/
int ByteBuffer::ReadLong (){

	return ByteBuffer::ReadBits(32);
}

/*
 ==================
 ByteBuffer::ReadFloat
 ==================
*/
float ByteBuffer::ReadFloat (){

	float	value;

	*(int *)&value = ByteBuffer::ReadBits(32);

	return value;
}

/*
 ==================
 ByteBuffer::ReadAngle8
 ==================
*/
float ByteBuffer::ReadAngle8 (){

	return BYTE2ANGLE((byte)ByteBuffer::ReadBits(8));
}

/*
 ==================
 ByteBuffer::ReadAngle16
 ==================
*/
float ByteBuffer::ReadAngle16 (){

	return SHORT2ANGLE((short)ByteBuffer::ReadBits(16));
}

/*
 ==================
 ByteBuffer::ReadDir
 ==================
*/
odVec3 ByteBuffer::ReadDir (){

	odVec3	dir;
	byte	bytes[2];

	bytes[0] = (byte)ByteBuffer::ReadBits(8);
	bytes[1] = (byte)ByteBuffer::ReadBits(8);

	M_BytesToNormal(bytes, dir);

	return dir;
}

/*
 ==================
 ByteBuffer::ReadString
 ==================
*/
int ByteBuffer::ReadString (char *string, int size){

	int		c, len = 0;

	while (1){
		c = (byte)ByteBuffer::ReadBits(8);

		if (c <= 0 || c >= 255)
			break;

		// Translate all fmt spec to avoid crash bugs
		if (c == '%')
			c = '.';

		// Don't allow higher ASCII values
		if (c > 127)
			c = '.';

		if (len < size - 1)
			string[len++] = c;
	}

	string[len] = 0;

	return len;
}

/*
 ==================
 ByteBuffer::ReadData
 ==================
*/
int ByteBuffer::ReadData (void *data, int size){

	int		count = this->readCount;
	int		i = 0;

	while (size >= 4){
		((uint *)data)[i++] = (uint)ByteBuffer::ReadBits(32);

		size -= 4;
	}

	i <<= 2;

	while (size){
		((byte *)data)[i++] = (byte)ByteBuffer::ReadBits(8);

		size--;
	}

	if (this->readCount > this->writeCount)
		return (this->writeCount - count) >> 3;

	return (this->readCount - count) >> 3;
}

/*
 ==================
 ByteBuffer::ReadDeltaChar
 ==================
*/
int ByteBuffer::ReadDeltaChar (int oldValue){

	if (!ByteBuffer::ReadBits(1))
		return (char)oldValue;

	return (char)ByteBuffer::ReadBits(8);
}

/*
 ==================
 ByteBuffer::ReadDeltaByte
 ==================
*/
int ByteBuffer::ReadDeltaByte (int oldValue){

	if (!ByteBuffer::ReadBits(1))
		return (byte)oldValue;

	return (byte)ByteBuffer::ReadBits(8);
}

/*
 ==================
 ByteBuffer::ReadDeltaShort
 ==================
*/
int ByteBuffer::ReadDeltaShort (int oldValue){

	if (!ByteBuffer::ReadBits(1))
		return (short)oldValue;

	return (short)ByteBuffer::ReadBits(16);
}

/*
 ==================
 ByteBuffer::ReadDeltaLong
 ==================
*/
int ByteBuffer::ReadDeltaLong (int oldValue){

	if (!ByteBuffer::ReadBits(1))
		return oldValue;

	return ByteBuffer::ReadBits(32);
}

/*
 ==================
 ByteBuffer::ReadDeltaFloat
 ==================
*/
float ByteBuffer::ReadDeltaFloat (float oldValue){

	float	value;

	if (!ByteBuffer::ReadBits(1))
		return oldValue;

	*(int *)&value = ByteBuffer::ReadBits(32);

	return value;
}

/*
 ==================
 ByteBuffer::ReadDeltaAngle8
 ==================
*/
float ByteBuffer::ReadDeltaAngle8 (float oldAngle){

	if (!ByteBuffer::ReadBits(1))
		return oldAngle;

	return BYTE2ANGLE((byte)ByteBuffer::ReadBits(8));
}

/*
 ==================
 ByteBuffer::ReadDeltaAngle16
 ==================
*/
float ByteBuffer::ReadDeltaAngle16 (float oldAngle){

	if (!ByteBuffer::ReadBits(1))
		return oldAngle;

	return SHORT2ANGLE((short)ByteBuffer::ReadBits(16));
}

/*
 ==================
 ByteBuffer::ReadDeltaDir
 ==================
*/
odVec3 ByteBuffer::ReadDeltaDir (const odVec3 &oldDir){

	odVec3	dir;
	byte	bytes[2];

	if (!ByteBuffer::ReadBits(1)){
		dir = oldDir;
		return dir;
	}

	bytes[0] = (byte)ByteBuffer::ReadBits(8);
	bytes[1] = (byte)ByteBuffer::ReadBits(8);

	M_BytesToNormal(bytes, dir);

	return dir;
}
